/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**@file          power_port.c
* @brief        电源模块 gpio 相关
* @details        依赖gpio.c、gpio.h
* @author
* @date         2021-11-12
* @version        V1.0
* @copyright      Copyright(c)2010-2021  JIELI
 */
#include "asm/power/power_port.h"
#include "asm/power_interface.h"
#include "asm/power/p33.h"
#include "asm/power/power_port.h"
#include "gpio.h"

void port_protect(u16 *port_group, u32 port_num)
{
    if (port_num == -1) {
        return;
    }
    port_group[port_num / IO_GROUP_NUM] &= ~BIT(port_num % IO_GROUP_NUM);
}

AT_VOLATILE_RAM_CODE
u32 spi_get_port(void)
{
    // sfc port
    return (JL_IOMAP->CON1 >> 5) & 0x1;
}

AT_VOLATILE_RAM_CODE
u32 spi_port_sel(void)
{
    u32 spi_port = 0;
    spi_port = spi_get_port() ? BIT(3) : BIT(2);
    return spi_port;
}

static u8 spi_bit_mode = 0;
AT_VOLATILE_RAM_CODE
u8 get_sfc_bit_mode()
{
    return spi_bit_mode;
}

#define READ_NORMAL_MODE                        0b0000  // CMD 1bit, ADDR 1bit clk < 30MHz
#define FAST_READ_NORMAL_MODE                   0b0001  // CMD 1bit, ADDR 1bit
#define FAST_READ_DUAL_IO_NORMAL_READ_MODE      0b0100  // CMD 1bit, ADDR 2bit
#define FAST_READ_DUAL_IO_CONTINUOUS_READ_MODE  0b0110  //  no cmd addr 2bit
#define FAST_READ_DUAL_OUTPUT_MODE              0b0010  // cmd 1bit addr 1bit
#define FAST_READ_QUAD_OUTPUT_MODE              0b0011  // cmd 1bit addr 1bit
#define FAST_READ_QUAD_IO_NORMAL_READ_MODE      0b0101  // cmd 1bit addr 4bit
#define FAST_READ_QUAD_IO_CONTINUOUS_READ_MODE  0b0111  // no cmd addr 4bit
void port_init(void)
{
    u16 port_die[] = {
        [PORTA_GROUP] = 0xffff,
        [PORTB_GROUP] = 0xffff,
        [PORTC_GROUP] = 0xffff,
        [PORTD_GROUP] = 0Xffff,
    };

    u16 port_dieh[] = {
        [PORTA_GROUP] = 0xffff,
        [PORTB_GROUP] = 0xffff,
        [PORTC_GROUP] = 0xffff,
        [PORTD_GROUP] = 0Xffff,
    };

    u8 usb_dp_keep = 0;
    u8 usb_dm_keep = 0;

    /***********************读寄存器************************/
    // 只判断sdtap_ch = 1;
    u32 sdtap_ch = -1;
    if (JL_SDTAP->CON & BIT(0)) {
        sdtap_ch = (JL_IOMAP->CON1 & 3 << 21) >> 21;
    }

    u8 port_sel = 0;
    if (p33_rx_1byte(P3_PINR_CON) & BIT(0)) {
        port_sel = 1;
    }

    // 只判断2线和4线，br系列没有1线
    u8 spi_mode = (JL_SFC->CON >> 8) & 0xf;

    switch (spi_mode) {
        case FAST_READ_QUAD_OUTPUT_MODE:
        case FAST_READ_QUAD_IO_NORMAL_READ_MODE:
        case FAST_READ_QUAD_IO_CONTINUOUS_READ_MODE:
        case 0b1001:
            spi_bit_mode = 4; // 4bit mode
            break;
        case FAST_READ_DUAL_OUTPUT_MODE:
        case FAST_READ_DUAL_IO_NORMAL_READ_MODE:
        case FAST_READ_DUAL_IO_CONTINUOUS_READ_MODE:
        case 0b1000:
            spi_bit_mode = 2;// 2bit moode
            break;
        default:
            spi_bit_mode = 1;
            // 1bit mode
            break;
    }

    u8 psram_en = JL_PSRAM->CON & BIT(0);

    /******************保留配置***************************/
    // sd_tap
    if (sdtap_ch == 1) {
        usb_dm_keep = 1;
        usb_dp_keep = 1;
    }

    // 长按复位
    if (port_sel) {
        port_sel = p33_rx_1byte(P3_PORT_SEL10);
        if ((port_sel >= 0) && (port_sel <= 15)) {
            port_protect(port_dieh, IO_PORTA_00 + port_sel);
        } else if ((port_sel >= 16) && (port_sel <= 31)) {
            port_protect(port_dieh, IO_PORTB_00 + port_sel - 16);
        } else if ((port_sel >= 32) && (port_sel <= 47)) {
            port_protect(port_dieh, IO_PORTC_00 + port_sel - 32);
        } else if ((port_sel >= 48) && (port_sel <= 55)) {
            port_protect(port_dieh, IO_PORTD_00 + port_sel - 48);
        } else if (port_sel == 56) {
            usb_dp_keep = 1;
        } else if (port_sel == 57) {
            usb_dm_keep = 1;
        }
    }

    // psram
    if (psram_en) {
        port_protect(port_die, PSRAM_D0A);
        port_protect(port_die, PSRAM_D1A);
        port_protect(port_die, PSRAM_D2A);
        port_protect(port_die, PSRAM_D3A);
    }

    // flash
    if (spi_get_port() == 0) {
        port_protect(port_die, SPI0_PWR_A);
        port_protect(port_die, SPI0_CS_A);
        port_protect(port_die, SPI0_CLK_A);
        port_protect(port_die, SPI0_DO_D0_A);
        port_protect(port_die, SPI0_DI_D1_A);
        if (get_sfc_bit_mode() == 4) {
            port_protect(port_die, SPI0_WP_D2_A);
            port_protect(port_die, SPI0_HOLD_D3_A);
        } else {
            gpio_set_pull_up(SPI0_WP_D2_A, 0);
            gpio_set_pull_up(SPI0_HOLD_D3_A, 0);
            gpio_set_pull_down(SPI0_WP_D2_A, 0);
            gpio_set_pull_down(SPI0_HOLD_D3_A, 0);
        }
    } else {
        port_protect(port_die, SPI0_PWR_B);
        port_protect(port_die, SPI0_CS_B);
        port_protect(port_die, SPI0_CLK_B);
        port_protect(port_die, SPI0_DO_D0_B);
        port_protect(port_die, SPI0_DI_D1_B);
        if (get_sfc_bit_mode() == 4) {
            port_protect(port_die, SPI0_WP_D2_B);
            port_protect(port_die, SPI0_HOLD_D3_B);
        } else {
            gpio_set_pull_up(SPI0_WP_D2_B, 0);
            gpio_set_pull_up(SPI0_HOLD_D3_B, 0);
            gpio_set_pull_down(SPI0_WP_D2_B, 0);
            gpio_set_pull_down(SPI0_HOLD_D3_B, 0);
        }
    }

    /**********************写配置****************************/
    gpio_die(GPIOA, 0, 16, ~port_die[PORTA_GROUP], GPIO_AND);
    gpio_dieh(GPIOA, 0, 16, ~port_dieh[PORTA_GROUP], GPIO_AND);

    gpio_die(GPIOB, 0, 16, ~port_die[PORTB_GROUP], GPIO_AND);
    gpio_dieh(GPIOB, 0, 16, ~port_dieh[PORTB_GROUP], GPIO_AND);

    gpio_die(GPIOC, 0, 16, ~port_die[PORTC_GROUP], GPIO_AND);
    gpio_dieh(GPIOC, 0, 16, ~port_dieh[PORTC_GROUP], GPIO_AND);

    gpio_die(GPIOD, 0, 16, ~port_die[PORTD_GROUP], GPIO_AND);
    gpio_dieh(GPIOD, 0, 16, ~port_dieh[PORTD_GROUP], GPIO_AND);

    // usb不用不要输出1
    if (!usb_dp_keep) {
        usb_iomode(1);
        JL_USB->CON0 = 0;
        JL_USB->CON1 = 0;
        gpio_set_direction(IO_PORT_DP, 1);
        gpio_set_pull_down(IO_PORT_DP, 0);
        gpio_set_pull_up(IO_PORT_DP, 0);
        gpio_set_die(IO_PORT_DP, 0);
        gpio_set_dieh(IO_PORT_DP, 0);
    }

    if (!usb_dm_keep) {
        usb_iomode(1);
        JL_USB->CON0 = 0;
        JL_USB->CON1 = 0;
        gpio_set_direction(IO_PORT_DM, 1);
        gpio_set_pull_down(IO_PORT_DM, 0);
        gpio_set_pull_up(IO_PORT_DM, 0);
        gpio_set_die(IO_PORT_DM, 0);
        gpio_set_dieh(IO_PORT_DM, 0);
    }

    // PR DIE default 0
    p33_tx_1byte(R3_PR_DIE, 0);
}

